using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.IO;
using TreeMaps.Controls;
using System.ComponentModel;
using System.Security.Permissions;

namespace Samples.Pages
{
  /// <summary>
  /// Interaction logic for DiskPage.xaml
  /// </summary>

  public partial class DiskPage : System.Windows.Controls.Page
  {
    private DriveInfo _currentDriveInfo;

    static DiskPage()
    {
      EventManager.RegisterClassHandler(typeof(DiskPage), PreviewMouseLeftButtonUpEvent, new RoutedEventHandler(MouseLeftUp));
    }
    public DiskPage()
    {
      InitializeComponent();

      DriveInfo[] list = DriveInfo.GetDrives();
      List<DriveInfo> drives = new List<DriveInfo>();
      foreach (DriveInfo drive in list)
      {
        if (drive.IsReady) drives.Add(drive);
      }
      _drives.ItemsSource = drives;

      _mode.ItemsSource = Enum.GetValues(typeof(TreeMapAlgo));
      _mode.SelectedItem = _tree.TreeMapMode;

      _maxDepth.Text = _tree.MaxDepth.ToString();
      _minArea.Text = _tree.MinArea.ToString();

    }

    #region private methods

    private void ClickRefresh(object sender, RoutedEventArgs e)
    {
      int maxDepth = 0;
      int.TryParse(_maxDepth.Text, out maxDepth);
      _maxDepth.Text = maxDepth.ToString();

      int minArea = 0;
      int.TryParse(_minArea.Text, out minArea);
      _minArea.Text = minArea.ToString();

      TreeMapAlgo algo = (TreeMapAlgo)_mode.SelectedItem;
      
      DriveInfo driveInfo = _drives.SelectedItem as DriveInfo;
      if (driveInfo == null) return;

      if (_currentDriveInfo != driveInfo)
      {
        _tree.ItemsSource = null;
        _tree.MaxDepth = maxDepth;
        _tree.MinArea = minArea;
        _tree.TreeMapMode = algo;

        this.SetStatus(string.Format("Scanning drive : {0} ...", driveInfo.Name));
        _progress.Visibility = Visibility.Visible;
        _progress.Value = 0;
        this.IsEnabled = false;

        BackgroundWorker worker = new BackgroundWorker();
        worker.WorkerReportsProgress = true;
        worker.DoWork += new DoWorkEventHandler(this.FillAsync);
        worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(this.FillEnd);
        worker.ProgressChanged += new ProgressChangedEventHandler(this.ProgressChanged);
        worker.RunWorkerAsync(driveInfo);
      }
      else
      {
        _tree.MaxDepth = maxDepth;
        _tree.MinArea = minArea;
        _tree.TreeMapMode = algo;
      }

      _currentDriveInfo = driveInfo;

    }

    private void ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
      _progress.Value = e.ProgressPercentage;
    }

    private void FillAsync(object sender, DoWorkEventArgs e)
    {
      DriveInfo drive = e.Argument as DriveInfo;

      List<DirItem> list = new List<DirItem>();

      double totalSize = 0;
      int percent = 0;
      foreach (DirectoryInfo di in drive.RootDirectory.GetDirectories())
      {
        try
        {
          FileIOPermission filePerm = new FileIOPermission(FileIOPermissionAccess.Read, di.FullName);
          string[] paths = filePerm.GetPathList(FileIOPermissionAccess.Read);
          if (paths != null && paths.Length == 1)
          {
            List<DirItem> children = null;
            double size = this.GetDirSize(sender as BackgroundWorker, di, ref percent, drive.TotalSize - drive.TotalFreeSpace, ref totalSize, out children);
            DirItem item = new DirItem(di.FullName, size, children);
            list.Add(item);
          }
        }
        catch (Exception ex)
        {
        }
      }
      e.Result = list;
    }

    private void FillEnd(object sender, RunWorkerCompletedEventArgs e)
    {
      _tree.ItemsSource = e.Result as List<DirItem>;
      _progress.Visibility = Visibility.Collapsed;
      this.SetStatus("Ready");
      this.IsEnabled = true;
    }

    internal void SetStatus(string text)
    {
      _status.Text = text;
    }

    private double GetDirSize(BackgroundWorker worker, DirectoryInfo root,ref int percent, double diskUsed,ref double totalSize, out List<DirItem> children)
    {

      children = new List<DirItem>();

      //System.Diagnostics.Debug.WriteLine("scanning " + root.FullName + "...");
      double size = 0;
      foreach (FileInfo fi in root.GetFiles())
      {
        try
        {
          size += fi.Length;
          totalSize += fi.Length;

          double newPercent = totalSize * 100 / diskUsed;
          if (newPercent - percent > 1)
          {
            percent = Convert.ToInt32(Math.Truncate(newPercent));
            worker.ReportProgress(percent);
          }

          children.Add(new DirItem(fi.FullName, fi.Length));
        }
        catch (Exception ex)
        {
        }
      }
      foreach (DirectoryInfo di in root.GetDirectories())
      {
        try
        {
          FileIOPermission filePerm = new FileIOPermission(FileIOPermissionAccess.Read, di.FullName);
          string[] paths = filePerm.GetPathList(FileIOPermissionAccess.Read);
          if (paths != null && paths.Length == 1)
          {
            List<DirItem> subElt = null;
            double childSize = this.GetDirSize(worker,di, ref percent,diskUsed, ref totalSize, out subElt);
            size += childSize;

            children.Add(new DirItem(di.FullName, childSize, subElt));
          }
        }
        catch (Exception ex)
        {
        }
      }

      if (children.Count == 0) children = null;

      return size;
    }

    #endregion

    #region static methods

    static void MouseLeftUp(object sender, RoutedEventArgs e)
    {
      string msg = "--";

      DependencyObject parent = VisualTreeHelper.GetParent(e.OriginalSource as DependencyObject);
      while (parent != null && !(parent is TreeMapItem))
      {
        parent = VisualTreeHelper.GetParent(parent);
      }
      if (parent is TreeMapItem)
      {
        TreeMapItem tItem = parent as TreeMapItem;
        double sizeInKo = Math.Round(((DirItem)tItem.DataContext).Value / 1024, 2);
        if (sizeInKo / 1024 > 512)
          msg = string.Format("{0} : {1} Go", ((DirItem)tItem.DataContext).Text, Math.Round(sizeInKo / 1024 / 1024, 2));
        else if (sizeInKo > 512)
          msg = string.Format("{0} : {1} Mo", ((DirItem)tItem.DataContext).Text, Math.Round(sizeInKo / 1024, 2));
        else
          msg = string.Format("{0} : {1} Ko", ((DirItem)tItem.DataContext).Text, sizeInKo);
      }
      (sender as DiskPage).SetStatus(msg);
    }

    #endregion
  }

  #region other classes

  public class DirItem
  {
    private string _text;
    private double _value;
    private List<DirItem> _subItems;

    public DirItem(string text, double value)
    {
      _text = text;
      _value = value;
    }

    public DirItem(string text, double value, List<DirItem> subItems)
      : this(text, value)
    {
      _subItems = subItems;
    }

    public double Value
    {
      get { return _value; }
      set { _value = value; }
    }

    public string Text
    {
      get { return _text; }
      set { _text = value; }
    }

    public List<DirItem> SubItems
    {
      get { return _subItems; }
    }
  }
  #endregion
}